import os
import requests
from typing import List, Dict
from fastapi import HTTPException
from app.models.movie import Movie
from dotenv import load_dotenv
from datetime import datetime
from app.config.settings import settings

# 加载环境变量（用于读取配置，如基础URL、超时时间等）
load_dotenv()

class MaoyanApiService:
    """猫眼API服务类：封装猫眼电影数据的获取和转换逻辑"""
    
    def __init__(self):
        """初始化服务：设置基础URL、请求头和超时时间"""
        # 从 settings 获取基础URL
        self.base_url = settings.maoyan_api_base_url
        # 从 settings 获取超时时间
        self.timeout = settings.maoyan_api_timeout
        # 模拟移动端浏览器请求头
        self.headers = {
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/1A543",
            "Referer": "https://m.maoyan.com/",  # 模拟从猫眼页面跳转
            "X-Requested-With": "XMLHttpRequest"  # 模拟AJAX请求
        }
        # 校验基础URL配置（确保服务可用）
        if not self.base_url:
            raise ValueError("请在.env文件中配置MAOYAN_API_BASE_URL（猫眼API基础地址）")

    def get_hot_movies(self, limit: int = 20) -> List[Dict]:
        """
        获取正在热映的电影列表（完整信息）
        
        核心逻辑：
        1. 调用猫眼热映列表API，获取热映电影的ID列表（movieIds）
        2. 根据ID逐个调用详情API，获取每部电影的完整信息
        3. 返回包含所有电影详情的列表
        
        :param limit: 最大返回数量（默认20部）
        :return: 热映电影详情列表（每部电影为字典格式）
        :raises HTTPException: 当获取ID列表或详情失败时抛出（500错误）
        """
        try:
            # 第一步：调用热映列表API，获取movieIds（热映电影的唯一标识）
            list_url = f"{self.base_url}/ajax/movieOnInfoList"
            list_response = requests.get(
                list_url,
                headers=self.headers,  # 使用预设请求头
                timeout=self.timeout   # 限制请求超时时间
            )
            # 检查请求是否成功（非200状态码会抛出异常）
            list_response.raise_for_status()
            # 解析返回的JSON数据
            list_data = list_response.json()
            
            # 提取热映电影ID列表，并截取前limit个（避免数据量过大）
            movie_ids = list_data.get("movieIds", [])[:limit]
            # 如果没有获取到ID，直接返回空列表（无热映电影）
            if not movie_ids:
                return []
            
            # 第二步：循环调用详情API，获取每部电影的完整信息
            hot_movies = []
            for movie_id in movie_ids:
                # 调用内部方法获取单部电影详情
                movie_detail = self.get_movie_detail(movie_id)
                # 将详情添加到结果列表
                hot_movies.append(movie_detail)
            
            return hot_movies
            
        except Exception as e:
            # 捕获所有异常（如网络错误、解析失败等），包装为HTTP 500错误
            raise HTTPException(
                status_code=500,
                detail=f"获取热映电影列表失败: {str(e)}"
            )

    def get_coming_soon_movies(self, ci: int = 10, limit: int = 10) -> List[Dict]:
        """
        获取即将上映的电影列表（完整信息）
        
        核心逻辑：
        1. 调用猫眼即将上映列表API，获取即将上映电影的ID列表
        2. 根据ID逐个调用详情API，获取每部电影的完整信息
        3. 返回包含所有电影详情的列表
        
        :param ci: 城市ID（默认为10）
        :param limit: 最大返回数量（默认10部）
        :return: 即将上映电影详情列表（每部电影为字典格式）
        :raises HTTPException: 当获取ID列表或详情失败时抛出（500错误）
        """
        try:
            # 第一步：调用即将上映列表API，获取电影ID列表
            list_url = f"{self.base_url}/ajax/comingList"
            list_response = requests.get(
                list_url,
                params={"ci": ci, "token": "", "limit": limit},
                headers=self.headers,
                timeout=self.timeout
            )
            # 检查请求是否成功
            list_response.raise_for_status()
            # 解析返回的JSON数据
            list_data = list_response.json()
            
            # 提取即将上映电影ID列表，并截取前limit个
            coming_movies = list_data.get("coming", [])[:limit]
            # 如果没有获取到电影，直接返回空列表
            if not coming_movies:
                return []
            
            # 第二步：循环调用详情API，获取每部电影的完整信息
            coming_soon_movies = []
            for movie in coming_movies:
                # 获取电影ID（即将上映API返回的ID字段可能不同，根据实际情况调整）
                movie_id = movie.get("id", 0)
                if movie_id:
                    # 调用内部方法获取单部电影详情
                    movie_detail = self.get_movie_detail(movie_id)
                    # 将详情添加到结果列表
                    coming_soon_movies.append(movie_detail)
                
            return coming_soon_movies
            
        except Exception as e:
            # 捕获所有异常，包装为HTTP 500错误
            raise HTTPException(
                status_code=500,
                detail=f"获取即将上映电影列表失败: {str(e)}"
            )
        
    def get_movie_detail(self, movie_id: int) -> Dict:
        """
        获取单部电影的完整详情（通过电影ID）
        
        核心逻辑：
        1. 调用猫眼电影详情API，传入电影ID
        2. 验证返回数据有效性（非空）
        3. 返回电影详情字典
        
        :param movie_id: 电影唯一标识（如1454962）
        :return: 电影详情字典（包含标题、评分、导演等信息）
        :raises HTTPException: 
            - 500错误：请求失败（如网络问题、403反爬拦截）
            - 404错误：返回数据为空（无此电影信息）
        """
        try:
            # 构建详情API地址
            detail_url = f"{self.base_url}/ajax/detailmovie"
            # 发送请求：传入电影ID作为参数
            response = requests.get(
                detail_url,
                params={"movieId": movie_id},  # 必须参数：指定电影ID
                headers=self.headers,          # 使用预设请求头（避免反爬）
                timeout=self.timeout           # 限制超时时间
            )
            # 检查请求状态
            response.raise_for_status()
            # 解析返回的JSON数据（包含电影详情）
            detail_data = response.json()
            
            # 验证数据有效性：如果返回空字典，抛出异常
            if not detail_data:
                raise ValueError(f"电影ID={movie_id}的详情数据为空")
            
            return detail_data
            
        except requests.exceptions.RequestException as e:
            # 捕获请求相关异常（如网络超时、404不存在）
            raise HTTPException(
                status_code=500,
                detail=f"请求电影详情失败（ID={movie_id}）：{str(e)}"
            )
        except ValueError as e:
            # 捕获数据为空的异常（返回404：资源不存在）
            raise HTTPException(
                status_code=404,
                detail=f"电影详情数据无效（ID={movie_id}）：{str(e)}"
            )

    def convert_to_hot_movie(self, maoyan_data: Dict) -> Movie:
        """
        将猫眼API返回的原始详情数据，转换为项目通用的Movie模型
        
        核心逻辑：
        1. 从原始数据中提取核心字段（标题、评分等）
        2. 适配猫眼API的字段命名（如"nm"对应标题，"sc"对应评分）
        3. 处理异常情况（字段缺失时使用默认值）
        
        :param maoyan_data: 猫眼详情API返回的原始数据（含detailMovie字段）
        :return: 转换后的Movie模型对象（符合项目数据规范）
        """

        # 从原始数据中提取detailMovie字段（猫眼详情数据的核心节点）
        detail_movie = maoyan_data.get("detailMovie", {})

        # 如果detailMovie为空（如API返回格式变更），返回默认模型
        if not detail_movie:
            return Movie(
                id=0,
                title="未知标题",
                year=2025,
                rating="暂无评分",
                director="未知导演",
                genre=["未知类型"],
                poster_url="",
                description="暂无简介",
            )
        raw_rating = detail_movie.get("sc", 0.0)
        rating = "暂无评分" if raw_rating == 0 else float(raw_rating)

        # 正常转换：按猫眼API字段映射到Movie模型
        return Movie(
            # 电影ID（猫眼用"id"标识）
            id=detail_movie.get("id", 0),
            # 电影标题（猫眼用"nm"标识，即"name"缩写）
            title=detail_movie.get("nm", "未知标题"),
            # 上映年份（从上映描述中提取，调用内部方法）
            year=self._extract_year(detail_movie.get("pubDesc", "")),
            # 电影评分（猫眼用"sc"标识，即"score"缩写）
            rating=rating,
            # 导演（猫眼用"dir"标识，即"director"缩写）
            director=detail_movie.get("dir", "未知导演"),
            # 电影类型（猫眼用"cat"标识，即"category"缩写，按逗号分割为列表）
            genre=detail_movie.get("cat", "").split(","),
            # 海报URL（猫眼用"img"标识，调整尺寸参数为300x450）
            poster_url=detail_movie.get("img", "").replace("thumbnail/2500x2500%3E", "300.450"),
            # 剧情简介（猫眼用"dra"标识，即"description"缩写）
            description=detail_movie.get("dra", "暂无简介"),
        )
    
    def _extract_year(self, pub_desc: str) -> int:
        """
        从上映描述中提取年份（适配猫眼pubDesc字段格式）
        
        核心逻辑：
        1. 优先从"YYYY-MM-DD"格式中提取年份（如"2025-06-27上映" → 2025）
        2. 提取失败时返回当前年份（避免无效值）
        
        :param pub_desc: 上映描述字符串（如"2025-06-27 09:00中国大陆上映"）
        :return: 提取的年份（整数）
        """
        try:
            # 处理常见格式："2025-06-27 09:00上映" → 按"-"分割取第一个部分
            if pub_desc and "-" in pub_desc:
                # 分割后取第一个元素（如"2025-06-27" → "2025"）
                year_str = pub_desc.split("-")[0]
                # 验证是否为4位数字（确保是有效年份）
                if len(year_str) == 4 and year_str.isdigit():
                    return int(year_str)
                
        except (IndexError, ValueError):
            # 忽略分割失败、非数字等异常（如pub_desc格式异常）
            pass
        
        # 所有提取逻辑失败时，返回当前年份（合理默认值）
        return datetime.now().year
    
    def convert_to_coming_soon_movie(self, maoyan_data: Dict) -> Movie:
        """
        将猫眼API返回的原始详情数据，转换为项目通用的Movie模型
        
        核心逻辑：
        1. 从原始数据中提取核心字段（标题、评分等）
        2. 适配猫眼API的字段命名（如"nm"对应标题，"sc"对应评分）
        3. 处理异常情况（字段缺失时使用默认值）
        
        :param maoyan_data: 猫眼详情API返回的原始数据（含detailMovie字段）
        :return: 转换后的Movie模型对象（符合项目数据规范）
        """

        # 从原始数据中提取detailMovie字段（猫眼详情数据的核心节点）
        detail_movie = maoyan_data.get("detailMovie", {}).get("detailMovie", {})
        
        # 如果detailMovie为空（如API返回格式变更），返回默认模型
        if not detail_movie:
            return Movie(
                id=0,
                title="未知标题",
                year=2025,
                rating="暂无评分",
                director="未知导演",
                genre=["未知类型"],
                poster_url="",
                description="暂无简介",
            )
        raw_rating = detail_movie.get("sc", 0.0)
        rating = "暂无评分" if raw_rating == 0 else float(raw_rating)

        # 正常转换：按猫眼API字段映射到Movie模型
        return Movie(
            # 电影ID（猫眼用"id"标识）
            id=detail_movie.get("id", 0),
            # 电影标题（猫眼用"nm"标识，即"name"缩写）
            title=detail_movie.get("nm", "未知标题"),
            # 上映年份（从上映描述中提取，调用内部方法）
            year=self._extract_year(detail_movie.get("pubDesc", "")),
            # 电影评分（猫眼用"sc"标识，即"score"缩写）
            rating=rating,
            # 导演（猫眼用"dir"标识，即"director"缩写）
            director=detail_movie.get("dir", "未知导演"),
            # 电影类型（猫眼用"cat"标识，即"category"缩写，按逗号分割为列表）
            genre=detail_movie.get("cat", "").split(","),
            # 海报URL（猫眼用"img"标识，调整尺寸参数为300x450）
            poster_url=detail_movie.get("img", "").replace("thumbnail/2500x2500%3E", "300.450"),
            # 剧情简介（猫眼用"dra"标识，即"description"缩写）
            description=detail_movie.get("dra", "暂无简介"),
        )
maoyanApiService = MaoyanApiService()